package com.stack.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.*;

/**
 * Des加解密工具类【浙江微能】【积分商城内嵌】
 *
 */
@Slf4j
public class DesUtil {

    public static final String ALL_CHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    public static void main(String[] args) {
//        System.out.println(generateStringByKey(32));
        String apiPassword = "Ib2G2YZkKtbqc9IUcG96Bhl71yqj8qdy";

        String data = "{\"name\":\"张三\"}";
        String enData = encryptBy3DES(data, apiPassword);
        String result = decryptBy3DES(enData, apiPassword);

        System.out.println(enData);
        System.out.println(result);
    }

    /**
     * 生成签名
     *
     * @param channelCode 账号
     * @param apiPassword 密钥
     * @param timestamp   时间戳
     * @param data        密文数据
     * @return
     */
    public static String generateSign(String channelCode, Long timestamp, String data, String apiPassword) {
        //参数转成map
        Map<String, Object> params = new HashMap<>();
        params.put("channelCode", channelCode);
        params.put("timestamp", timestamp);
        params.put("data", data);
        params.put("apiPassword", apiPassword);
        //自然排序后拼接参数值
        String signString = map2SignString(params);
        log.info("签名原串: {}", signString);
        //生成签名
        String sign = getMD5(signString);
        log.info("签名: {}", sign);
        return sign;
    }

    /**
     * 生成签名
     *
     * @param channelCode 账号
     * @param apiPassword 密钥
     * @param timestamp   时间戳
     * @param data        密文数据
     * @param sign        签名
     * @return true/false
     */
    public static boolean checkSign(String channelCode, Long timestamp, String data, String sign, String apiPassword) {
        String newSign = generateSign(channelCode, timestamp, data, apiPassword);
        return sign.equals(newSign);
    }

    public static String map2SignString(Map map) {
        if (map == null || map.isEmpty()) {
            return null;
        }

        Map<String, Object> paramMap = sortByKey(map);
        StringBuilder content = new StringBuilder();
        for (String key : paramMap.keySet()) {
            String value = paramMap.get(key).toString();
            if (StringUtils.isNotBlank(value)) {
                content.append(value);
            }
        }
        return content.toString();
    }

    public static Map<String, Object> sortByKey(Map map) {
        if (map == null || map.isEmpty()) {
            return null;
        }
        Map<String, Object> sortMap = new TreeMap<>(Comparator.naturalOrder());
        sortMap.putAll(map);
        return sortMap;
    }

    /**
     * 返回一个定长的带因子的固定的随机字符串(只包含大小写字母、数字)
     *
     * @param length 随机字符串长度
     * @return 随机字符串
     */
    public static String generateStringByKey(int length) {
        StringBuffer sb = new StringBuffer();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            sb.append(ALL_CHAR.charAt(random.nextInt(ALL_CHAR.length())));
        }
        return sb.toString();
    }

    /**
     * 3重DES加密
     *
     * @param data
     * @param desKey 密钥长度不少于24的倍数位
     * @return
     */
    public static String encryptBy3DES(String data, String desKey) {
        String result = null;
        try {
            SecureRandom secureRandom = new SecureRandom();
            DESedeKeySpec sedeKeySpec = new DESedeKeySpec(desKey.getBytes());

            SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DESede");
            SecretKey key = secretKeyFactory.generateSecret(sedeKeySpec);

            Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key, secureRandom);

            byte[] bytesresult = cipher.doFinal(data.getBytes());
            result = new sun.misc.BASE64Encoder().encode(bytesresult);
        } catch (Exception e) {
            log.error("EncryptBy3DES加密失败，data = " + data, e);
        }
        return result;
    }

    /**
     * 3重DES解密
     *
     * @param data
     * @param desKey
     * @return
     */
    public static String decryptBy3DES(String data, String desKey) {
        String deresult = null;
        try {
            SecureRandom secureRandom = new SecureRandom();
            DESedeKeySpec sedeKeySpec = new DESedeKeySpec(desKey.getBytes());

            SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DESede");
            SecretKey key = secretKeyFactory.generateSecret(sedeKeySpec);

            Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key, secureRandom);

            byte[] bytesresult = cipher.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(data));
            deresult = new String(bytesresult);
        } catch (Exception e) {
            log.error("decryptBy3DES解密失败，data = " + data, e);
        }
        return deresult;
    }

    public static String getMD5(String encrypt) {
        String md5 = "";

        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] encryptByte = encrypt.getBytes("UTF-8");
            byte[] md5Byte = md.digest(encryptByte);
            md5 = bytesToHex(md5Byte);
        } catch (Exception var5) {
            var5.printStackTrace();
        }

        return md5;
    }

    public static String bytesToHex(byte[] bytes) {
        StringBuffer hexStr = new StringBuffer();

        for (int i = 0; i < bytes.length; ++i) {
            int num = bytes[i];
            if (num < 0) {
                num += 256;
            }

            if (num < 16) {
                hexStr.append("0");
            }

            hexStr.append(Integer.toHexString(num));
        }

        return hexStr.toString().toUpperCase();
    }


}
